package date;

import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;

import java.text.DateFormatSymbols;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.util.*;

/**
 * 日期工具类
 */
public class DateUtil {


    public static void main(String[] args) throws Exception {

        List<LocalDate> dateList = new ArrayList<>();
        dateList.add(LocalDate.parse("2018-01-11"));
        dateList.add(LocalDate.parse("2017-02-19"));
        dateList.add(LocalDate.parse("2017-12-01"));
        dateList.add(LocalDate.parse("2016-12-09"));
        dateList.add(LocalDate.parse("2020-12-22"));
        dateList.add(LocalDate.parse("2024-06-13"));
        dateList.add(LocalDate.parse("1992-11-28"));
        dateList.stream().sorted().forEach(System.out::println);

        System.out.println("1".toUpperCase()); // 1
        System.out.println(numberToChineseConverter(1)); // 一  只能处理1  (int)'一' 19968
        System.out.println(numberToChineseConverter(2)); // 丁  (int)'丁' 19969  (int)'二' 20108
        System.out.println(numberToChineseConverter(9)); // 丈  (int)'三' 19977

        System.out.println(generateRandomChinese(3));

        System.out.println(numberToString(11456)); // 一一四五六
        System.out.println(numberToString(1145600)); // 一一四五六

        System.out.println(toUnicode("我是一个haoren，11456")); // 一一四五六

        /**
         * https://blog.csdn.net/m0_37739193/article/details/78309225
         * M	将月份显示为不带前导零的数字(如：一月表示为1，十二月表示为12)
         * MM	将月份显示为带前导零的数字(如：一月表示为01，十二月表示为12)
         * MMM	将月份显示为英文缩写形式(如：Jan)
         * MMMM	将月份显示为完整月份名(如：January)
         * E	将星期显示为英文缩写形式(如：Sun)
         * EEEE	将星期显示为完整星期名(如：Sunday)
         * d	将日显示为不带前导零的数字(如：1、23)
         * dd	将日显示为带前导零的数字(如：01、23)
         */
        SimpleDateFormat simpleDateFormat0 = new SimpleDateFormat("yyyy-MM-dd");

        // 根据系统语言环境
        SimpleDateFormat simpleDateFormatDefault = new SimpleDateFormat("dd-MMMM-yyyy");
        // 中文
        SimpleDateFormat simpleDateFormatZH = new SimpleDateFormat("dd-MMMM-yyyy MMM M月 EEEE E", Locale.SIMPLIFIED_CHINESE);
        // 英语
        SimpleDateFormat simpleDateFormatEN = new SimpleDateFormat("dd-MMMM-yyyy MMM M月 EEEE E", Locale.US);

        // 13-十二月-2023 十二月 12月 星期三 星期三
        System.out.println(simpleDateFormatZH.format(new Date()));
        // 13-December-2023 Dec 12月 Wednesday Wed
        System.out.println(simpleDateFormatEN.format(new Date()));

        String pattern = simpleDateFormatEN.toPattern(); // 合成一个表示此 Format 对象当前状态的模式字符串
        String localizedPattern = simpleDateFormatEN.toLocalizedPattern(); // 合成一个表示此 Format 对象当前状态的、已本地化的模式字符串。

        DateFormatSymbols symbols = simpleDateFormatDefault.getDateFormatSymbols();
        // 月份全称 一月
        String[] months = symbols.getMonths();
        // 月份全称 January
        String[] monthsUs = simpleDateFormatEN.getDateFormatSymbols().getMonths();
        // 月份简称 一月 Jan
        String[] shortMonths = symbols.getShortMonths();

        // 星期全称 星期一 Monday
        String[] weekdays = symbols.getWeekdays();
        // 星期简称 星期一 Mon
        String[] shortWeekdays = symbols.getShortWeekdays();

        // 修改月份全称
        for (int i = 0; i < months.length; i++) {
            // 默认的已经是一月 而且数字的大写并不能转为一二三...
            if (months[i] != null && !months[i].equals("")) {
                months[i] = months[i].toUpperCase() + "份";
            }
        }

        // 必须要重新设置回去才会生效
        symbols.setMonths(months);
        simpleDateFormatDefault.setDateFormatSymbols(symbols);

        // 13-十二月份-2023
        System.out.println(simpleDateFormatDefault.format(new Date()));

        // Tue Jul 12 00:00:00 CST 2022
        System.out.println(simpleDateFormatDefault.parse("12-七月份-2022"));
        // 也可以处理，没有报错 Tue Jul 12 00:00:00 CST 2022
        System.out.println(simpleDateFormatDefault.parse("12-七月-2022"));

        // 夏令时（Daylight Saving Time，简称DST）问题 https://blog.csdn.net/m0_37539286/article/details/130445667
        // 夏令时是一种将时间调快一小时的做法，通常在夏季实施
        Date date0 = simpleDateFormat0.parse("1988-04-15");
        Date date00 = simpleDateFormat0.parse("1989-06-18");

        TimeZone tz = TimeZone.getTimeZone("Europe/Berlin"); // 柏林时区 UTC +1:00
        TimeZone tz1 = TimeZone.getDefault(); // 获取系统默认时区 GMT+08:00
        System.out.println(tz1);
        
        boolean b = tz.inDaylightTime(date0); // 判断当前日期是否处于夏令时
        boolean b1 = tz1.inDaylightTime(date0);
        boolean bb = tz.inDaylightTime(date00);
        boolean bb1 = tz1.inDaylightTime(date00);

        // 时区 Asia/Shanghai  存在夏令时   进入夏令时 时钟快一个小时，离开夏令时 时钟慢一个小时
        // GMT 标准时，不存在夏令时
        SimpleDateFormat simpleDateFormat00 = new SimpleDateFormat("yyyy-MM-dd");
        simpleDateFormat00.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
        System.out.println(simpleDateFormat00.parse("1989-06-18")); // Sat Jun 17 23:00:00 GMT+08:00 1989

        // simpleDateFormat00.setTimeZone(TimeZone.getTimeZone("GMT+08:00"));
        simpleDateFormat00.setTimeZone(TimeZone.getTimeZone("GMT+8"));
        System.out.println(simpleDateFormat00.parse("1989-06-18")); // Sun Jun 18 00:00:00 GMT+08:00 1989

        // System.out.println(simpleDateFormatDefault.format(date));
        // System.out.println(simpleDateFormatDefault.format(new Date())); // 11-十二月-2023
        // System.out.println(simpleDateFormatZH.format(new Date())); // 11-十二月-2023 星期一  十二月 12月 星期一  简体中文，月份和星期的全称和简称是一样的
        // System.out.println(simpleDateFormatEN.format(new Date())); // 11-December-2023 Monday Dec 12月 Mon  英语
        // System.out.println(simpleDateFormatEN.format(date)); // 12-July-2022 Tuesday Jul 7月 Tue  英语


        TimeZone timeZone = TimeZone.getDefault();
        testDayTime(timeZone);

        System.out.println("----------------------------------------------------------------");

        timeZone = TimeZone.getTimeZone("GMT");
        testDayTime(timeZone);


    }

    // java夏令时问题呈现(中国也存在夏令时)
    // https://blog.csdn.net/u011165335/article/details/76636296
    // https://codeleading.com/article/88563621020/
    // 1986年至1991年,中华人民共和国在全国范围实行了六年夏令时,每年从4月中旬的第一个星期日2时整(北京时间)到9月中旬第一个星期日的凌晨2时整;
    // https://www.cnblogs.com/qvennnnn/p/13919543.html java 日期数据前端比数据库少一天问题
    public static void testDayTime(TimeZone timeZone) {

        SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

        System.out.println("Time Zone is " + timeZone.getDisplayName() + " " + timeZone.getID());

        Calendar start = Calendar.getInstance(timeZone);
        start.setTime(new Date(0));//UTC 1970-01-01

        System.out.println("start=" + fmt.format(start.getTime()));

        long end = Calendar.getInstance(timeZone).getTimeInMillis();//current time

        boolean find = false;
        for (long i = start.getTimeInMillis(); i < end; i = start.getTimeInMillis()) {
            start.add(Calendar.DATE, 1); //add one day

            if ((start.getTimeInMillis() - i) % (24 * 3600 * 1000L) != 0) {
                find = true;
                System.out.println("from " + fmt.format(new Date(i)) +
                        "to " + fmt.format(start.getTime()) +
                        " has " + (start.getTimeInMillis() - i) + "ms" +
                        "[" + (start.getTimeInMillis() - i) / (3600 * 1000L) + "hours]");
            }
        }
        if (!find) {
            System.out.println("Every day is ok.");
        }
    }


    /**
     * java怎么把数字的1转换成汉字的一
     * https://blog.51cto.com/u_16175522/6948414
     * 方案四：使用Unicode编码，将数字转换成对应的汉字。
     * 我们知道Unicode中汉字的编码范围是从0x4E00开始，
     * 而数字1的编码是0x4E00-1，因此我们可以通过将数字加上0x4E00-1来得到对应的汉字。
     */
    public static String numberToChineseConverter(int number) {
        return new String(Character.toChars(number + 0x4E00 - 1));
    }

    /**
     * Java如何随机生成汉字 https://blog.51cto.com/u_16213353/7509468
     * 解决方案
     * 要实现随机生成汉字的功能，我们需要了解汉字的编码规则和Java中字符串的操作方法。
     * <p>
     * 汉字的编码规则
     * 汉字的编码采用Unicode编码，每个汉字占用2个字节。Unicode编码将每个字符都分配了一个唯一的整数值，用来表示该字符。
     * <p>
     * 汉字的Unicode编码范围是：[0x4E00, 0x9FA5]，该范围内包含了常用的汉字。
     *
     * @param count
     * @return
     */
    public static String generateRandomChinese(int count) {
        // "0x" 是Java中表示十六进制数的前缀
        final int START = 0x4E00; // 19968
        final int END = 0x9FA5; // 40869

        StringBuilder sb = new StringBuilder();
        Random random = new Random();

        for (int i = 0; i < count; i++) {
            int code = random.nextInt(END - START + 1) + START;
            // char：16位，存储Unicode码，用单引号赋值 https://blog.51cto.com/u_12902/6541780
            // 如果低级类型为char型，向高级类型（整型）转换时，会转换为对应ASCII码值
            // 对于byte,short,char三种类型而言，他们是平级的，因此不能相互自动转换，可以使用下述的强制类型转换
            sb.append((char) code);
        }

        return sb.toString();
    }


    /**
     * Java 将数字123对应于中文的一二三 https://blog.csdn.net/hlx20080808/article/details/50469699
     *
     * @param number 数字
     * @return
     */
    private static String numberToString(int number) {
        final char[] cs = "零一二三四五六七八九".toCharArray();
        String temp = "";
        // 从后往前取
        while (number > 0) {
            temp += cs[number % 10]; // 取余数 取最后一位
            System.out.println(temp);
            number /= 10; // 取商 去掉最后一位
        }

        System.out.println(temp);
        return StringUtils.reverse(temp);
    }

    /**
     * Java中的字符串转Unicode方法 https://blog.51cto.com/u_16213346/7749667
     * Unicode编码通常用\\u加上四位或六位的十六进制数来表示一个字符。例如，字母A的Unicode编码是\u0041。
     * 非法的 Unicode 转义
     *
     * @return
     */
    public static String toUnicode(String str) throws Exception {
        // 使用Unicode转义序列 Java允许在字符串中使用Unicode转义序列来表示Unicode字符
        StringBuilder unicode = new StringBuilder();
        for (char c : str.toCharArray()) {
            unicode.append("\\u").append(String.format("%04x", (int) c));
        }
        System.out.println(unicode.toString());

        // 使用Java内置函数 https://blog.51cto.com/u_16175447/7199590
        // Java提供了String类的getBytes()和new String()方法，可以实现字符串与Unicode编码的相互转换。
        // 使用getBytes()方法将字符串转换为字节数组，再将字节数组转换为Unicode编码
        unicode = new StringBuilder();
        byte[] bytes = str.getBytes();
        for (byte b : bytes) {
            unicode.append("\\u").append(String.format("%02X", b));
        }
        System.out.println(unicode);

        // 使用Java的Character类
        // Java的Character类提供了一些方法用于处理字符和Unicode编码。可以使用Character类的toChars方法将字符转换为Unicode编码的字符数组
        unicode = new StringBuilder();
        for (char c : str.toCharArray()) {
            char[] chars = Character.toChars(c);
            for (char ch : chars) {
                unicode.append(String.format("\\u%04x", (int) ch));
            }
        }
        System.out.println(unicode.toString());

        // Unicode编码转换为字符串
        // 我们可以使用new String()方法将Unicode编码转换为字符串
        String unicodeTmp = unicode.toString().replace("\\u", "");
        StringBuilder strTmp = new StringBuilder();
        for (int i = 0; i < unicodeTmp.length(); i += 4) {
            String codePoint = unicodeTmp.substring(i, i + 4);
            strTmp.append((char) Integer.parseInt(codePoint, 16));
        }
        System.out.println(strTmp);

        // 使用第三方库 commons-lang3 使用escapeJava()方法将字符串Hello World!转换为Unicode编码，并使用unescapeJava()方法将Unicode编码
        String str1 = StringEscapeUtils.escapeJava(str);
        System.out.println(str1);

        String str2 = StringEscapeUtils.unescapeJava(str1);
        System.out.println(str2);

        return unicode.toString();
    }

}
